#include <iostream>
#include <string>
#include <vector>
#include <boost/filesystem.hpp> //bbost中的文件处理的库
#include "util.hpp"
const std::string src_path = "data/input/";
const std::string out_path = "data/raw_html/output.txt";

typedef struct DocInfo_t
{
    std::string title;
    std::string content;
    std::string url;
} DocInfo_t;

/* 参数的三种格式
1. const& 输入参数
2. *      输出参数
3. &      输入输出参数
*/
bool GetFilesList(const std::string &src_path, std::vector<std::string> *files_list);
bool ParseHtml(const std::vector<std::string> &files_list, std::vector<DocInfo_t> *results);
bool SaveHtml(const std::vector<DocInfo_t> &results, const std::string &out_path);
// 2. 从文件中提取titile
bool GetTitle(const std::string &file_content, std::string *title)
{
    const std::string head_label = "<title>";
    const std::string end_label = "</title>";
    size_t begin = file_content.find(head_label);
    if (begin == std::string::npos)
    {
        std::cerr << "未找到开头标题标签" << std::endl;
        return false;
    }
    size_t end = file_content.find(end_label);
    if (end == std::string::npos)
    {
        std::cerr << "未找到结尾标题标签" << std::endl;
        return false;
    }
    *title = file_content.substr(begin + head_label.size(), end - (begin + head_label.size()));
    return true;
}
// 3. 从文件中提取content
bool GetContent(const std::string &file_content, std::string *content)
{
    // 简易版状态机
    enum sta
    {
        LABLE,
        CONTENT
    };
    enum sta status = LABLE; // 默认是便签状态
    for (char c : file_content)
    {
        switch (status)
        {
        case LABLE:
            if (c == '>') status = CONTENT;  //接下来就是数据了
            break;
        case CONTENT:
            if (c == '<') status = LABLE; //接下来就是标签了
            else 
            {
                if (c == '\n') c = ' '; //把换行替换为空格
                *content += c;
            }
            break;
        default:
            break;
        }
    }
    return true;
}
// 4. 从文件中提取url
bool GetUrl(const std::string& file_path,std::string* url)
{
    std::string head_url = "https://www.boost.org/doc/libs/1_88_0/doc/html/";
    std::string tail_url = file_path.substr(src_path.size()); //我们这里得到的就是类似accumulators.html的字符串
    if (tail_url.empty()){
        std::cerr << "文件路径后缀为空" << std::endl;
        return false;
    }
    *url = (head_url + tail_url);
    return true;
}

int main()
{
    std::vector<std::string> files_list;
    std::vector<DocInfo_t> results;
    // 1. 获取所有的文件路径
    if (!GetFilesList(src_path, &files_list))
    {
        std::cerr << "文件路径获取失败" << std::endl;
        return 1;
    }
    std::cout << "以获取文件路径的个数: " << files_list.size() << std::endl;
    // 2. 读取所有的文件，对数据进行去标签，解析数据
    if (!ParseHtml(files_list, &results))
    {
        std::cerr << "文件解析失败" << std::endl;
        return 2;
    }
    // 3. 把得到的数据，按照一个文档 \n 一个文档\n 这样的方式写在同一个文件当中
    if (!SaveHtml(results, out_path))
    {
        std::cerr << "解析内容保存失败" << std::endl;
        return 3;
    }
    return 0;
}

void Show(DocInfo_t& doc)
{
    std::cout << "############################################################" << std::endl;
    std::cout << "title: " << doc.title << std::endl;
    std::cout << "content: " << doc.content << std::endl;
    std::cout << "url: " << doc.url << std::endl;
    std::cout << "############################################################" << std::endl;
}
bool GetFilesList(const std::string &src_path, std::vector<std::string> *files_list)
{
    // 1. 从stc_path这个路径中查找所有的文件路径
    namespace fs = boost::filesystem;
    // 构建path对象
    fs::path root_path(src_path);
    // 先判断源路径是否存在
    if (!fs::exists(root_path))
    {
        std::cerr << "源文件路径不存在！" << std::endl;
        return false;
    }
    fs::recursive_directory_iterator end; // 相当于迭代器中中的最后一个迭代器
    // 遍历
    for (fs::recursive_directory_iterator iter(root_path); iter != end; ++iter)
    {
        // 如果是一个文件的话
        if (!iter->is_regular_file())
        {
            // std::cerr << "不是一个正常的文件" << std::endl;
            continue;
        }
        if (iter->path().extension() != ".html")
        {
            // 注意返回值啊，蠢货
            // std::cerr << "不是html文件" << std::endl;
            continue;
        }
        // 到了这个地方说明，我们得到了一个html文件路径
        //  std::cout << "debug path: " << iter->path().string() << std::endl;
        files_list->push_back(std::move(iter->path().string())); // 但是push_back会有拷贝消耗，所以我们使用move，进行移动拷贝
    }
    return true;
}
bool ParseHtml(const std::vector<std::string> &files_list, std::vector<DocInfo_t> *results)
{
    for (const std::string &file_path : files_list)
    {
        // 1. 从文件中读取数据
        std::string file_content;
        if (!ns_util::file_util::ReadFile(file_path, &file_content))
        {
            std::cerr << "文件内容获取失败" << std::endl;
            continue;
        }
        DocInfo_t doc;
        // 2. 从文件中提取titile
        if (!GetTitle(file_content, &doc.title))
        {
            std::cerr << "文件标题获取失败" << std::endl;
            continue;
        }
        // 3. 从文件中提取content
        if (!GetContent(file_content,&doc.content))
        {
            std::cerr << "文件内容获取失败" << std::endl;
            continue;
        }
        // 4. 从文件中提取url
        if (!GetUrl(file_path,&doc.url))
        {
            std::cerr << "文件url获取失败" << std::endl;
            continue;
        }
        //for debug
        // if (results->size() == 6957){
        //     std::cout << "file path: " << file_path << std::endl;
        //     std::cout << "content: " << doc.content << std::endl;
        // }
        //for debug
        // Show(doc);
        // break; 
        results->push_back(std::move(doc));
    }
    return true;
}
bool SaveHtml(const std::vector<DocInfo_t> &results, const std::string &out_path)
{
    const std::string sep = "\3";
    std::ofstream out(out_path,std::ios::binary | std::ios::out);
    if (!out.is_open()) {
        std::cerr << "目标文件不存在!!，请检查路径是否正确" << std::endl;
        return false;
    }
    for (const DocInfo_t& doc : results)
    {
        //使用\3来隔绝每一个文档内的三个内容
        std::string content = doc.title;
        content += sep;
        content += doc.content;
        content += sep;
        content += doc.url;
        //使用\n来隔绝各个文档
        content += '\n';
        out.write(content.c_str(),content.size());
    }
    return true;
}
